Первые шаги с Cassandra
Разработчику
Аналитику
Тестировщику
Архитектору
Инженеру
Установка системы Apache Cassandra
Процесс установки требует наличия среды выполнения Java (JDK), так как Cassandra написана на Java. Для работы необходим серверный компонент (cassandra) и клиентские утилиты (cqlsh).
Требования к системе
- Операционная система: Linux (рекомендуется Ubuntu/CentOS), macOS или Windows.
- Ява (JDK): Версия 8, 11 или 17 (в зависимости от версии Cassandra).
- Права администратора на машине.
- Минимум 2 ГБ оперативной памяти.
- Доступ к интернету для загрузки пакетов.
Алгоритм установки
Вариант А: Установка на Linux (на примере Ubuntu/Debian)
Откройте терминал и выполните следующие команды для добавления репозитория и установки:
sudo apt update
sudo apt install openjdk-11-jdk
echo "deb http://www.apache.org/dist/cassandra/debian 41x main" | sudo tee -a /etc/apt/sources.list.d/cassandra.sources.list
curl https://www.apache.org/dist/cassandra/KEYS | sudo apt-key add -
sudo apt-get update
sudo apt-get install cassandra
После завершения установки проверьте статус службы:
sudo systemctl status cassandra
Для входа в интерактивную оболочку используйте команду:
cqlsh
Вариант Б: Использование Docker (универсальный способ)
Создание контейнера позволяет быстро развернуть окружение без сложной настройки зависимостей:
docker run --name my-cassandra \
-d \
-p 9042:9042 \
-p 7000:7000 \
-p 7001:7001 \
-e CASSANDRA_CLUSTER_NAME='MyCluster' \
cassandra:latest
Подключение к контейнеру выполняется командой:
docker exec -it my-cassandra cqlsh
Создание ключевых пространств (Keyspace)
В Cassandra аналогом базы данных является KeySpace (ключевое пространство). Оно определяет параметры репликации и стратегию хранения данных. В отличие от SQL, создание KeySpace обязательно перед созданием таблиц.
Командный способ
Выполните команду в терминале cqlsh:
CREATE KEYSPACE company_db
WITH replication = {
'class': 'SimpleStrategy',
'replication_factor': 1
};
Примечание: Параметр replication_factor указывает количество копий данных. Значение 1 означает одну копию (для тестирования). В продакшене обычно используют 3.
Выбор стратегии репликации
| Стратегия | Описание | Сценарий использования |
|---|---|---|
| SimpleStrategy | Реплицирует данные на соседние узлы в порядке их следования. | Одноразовые кластеры, тестовые среды. |
| NetworkTopologyStrategy | Позволяет контролировать репликацию по зонам availability (AZ) и регионам. | Продвинутые кластеры, требующие отказоустойчивости. |
Переключение контекста на созданное пространство:
USE company_db;
Создание таблицы и первичного ключа
Таблица в Cassandra — это логическая структура, но её проектирование критически зависит от модели запросов. Данные не хранятся в произвольном порядке, а сортируются согласно Первичному ключу.
Синтаксис создания таблицы
Первичный ключ состоит из Partition Key (ключ партиции) и Clustering Columns (кластеризующие колонки).
CREATE TABLE employees (
employee_id UUID PRIMARY KEY,
first_name TEXT,
last_name TEXT,
email TEXT,
hire_date TIMESTAMP,
salary DECIMAL,
department_id INT
);
Анализ компонентов определения
- employee_id: Тип
UUIDгарантирует уникальность без внешних генераторов. Это Partition Key. Все данные с одним ID будут находиться на одном узле. - first_name, last_name, email: Текстовые поля (
TEXT). - hire_date: Тип
TIMESTAMPдля хранения даты и времени. - salary: Тип
DECIMALдля точных финансовых вычислений. - department_id: Целочисленное поле.
Проектирование Первичного ключа
В Cassandra нельзя писать SELECT * WHERE column != partition_key. Запросы всегда должны начинаться с Partition Key.
Пример таблицы с составным ключом для частого поиска по отделу:
CREATE TABLE employees_by_dept (
department_id INT,
employee_id UUID,
first_name TEXT,
last_name TEXT,
salary DECIMAL,
PRIMARY KEY (department_id, employee_id)
);
Здесь department_id — Partition Key, а employee_id — Clustering Column. Данные внутри отдела будут отсортированы по ID сотрудника.
Добавление ограничений и индексов
Cassandra имеет строгие ограничения целостности. Внешние ключи не поддерживаются напрямую. Индексы существуют, но их использование ограничено.
Ограничения уникальности
Уникальность обеспечивается только частью Первичного ключа. Если попытаться вставить дубликат employee_id, возникнет ошибка.
Для обеспечения уникальности других полей (например, email) необходимо создать отдельную таблицу-справочник:
CREATE TABLE emails_lookup (
email TEXT PRIMARY KEY,
employee_id UUID
);
Создание индексов
Индексы позволяют искать данные по непартиционным колонкам, но они могут снижать производительность при больших объемах. Рекомендуется использовать их только для фильтрации, а не для JOIN.
Создание обычного индекса по фамилии:
CREATE INDEX idx_employees_last_name ON employees(last_name);
Создание составного индекса (только если колонки часто используются вместе в WHERE):
CREATE INDEX idx_dept_salary ON employees(department_id, salary);
Важно: В Cassandra индексные запросы работают медленно на больших данных. Предпочтительнее проектировать таблицы так, чтобы все нужные запросы покрывались Первичным ключом.
Выполнение CRUD операций
CRUD в Cassandra реализован через язык CQL (Cassandra Query Language).
Создание записей (Create)
Вставка нового сотрудника:
INSERT INTO employees (employee_id, first_name, last_name, email, hire_date, salary, department_id)
VALUES (uuid(), 'Ivan', 'Ivanov', 'ivanov@example.com', toTimestamp(now()), 75000.00, 1);
Вставка с явным указанием UUID:
INSERT INTO employees (employee_id, first_name, last_name, email, hire_date, salary, department_id)
VALUES ('a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11', 'Maria', 'Petrova', 'petrova@example.com', '2024-02-10', 82000.00, 2);
Использование IF NOT EXISTS для проверки существования:
INSERT INTO employees (employee_id, first_name, last_name, email, hire_date, salary, department_id)
VALUES (uuid(), 'Alexey', 'Sidorov', 'sidorov@example.com', toTimestamp(now()), 65000.00, 1)
IF NOT EXISTS;
Чтение данных (Read)
Выборка конкретного сотрудника по ID (самый быстрый запрос):
SELECT * FROM employees WHERE employee_id = 'a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11';
Выборка всех сотрудников определенного отдела (требуется указание Partition Key):
SELECT * FROM employees_by_dept WHERE department_id = 1;
Фильтрация с использованием индекса (менее эффективно):
SELECT * FROM employees WHERE last_name = 'Ivanov';
Сортировка результатов внутри партиции:
SELECT * FROM employees_by_dept WHERE department_id = 1 ORDER BY employee_id DESC;
Обновление данных (Update)
Изменение зарплаты сотрудника:
UPDATE employees SET salary = 80000.00 WHERE employee_id = 'a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11';
Обновление с проверкой условия (условная запись):
UPDATE employees SET salary = 85000.00
WHERE employee_id = 'a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11' AND salary < 80000.00
IF EXISTS;
Удаление данных (Delete)
Удаление конкретного сотрудника:
DELETE FROM employees WHERE employee_id = 'a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11';
Удаление конкретной колонки:
DELETE email FROM employees WHERE employee_id = 'a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11';
Очистка всей таблицы (осторожно!):
TRUNCATE TABLE employees;
Создание представлений (Materialized Views)
В Cassandra нет традиционных представлений (VIEW). Вместо них используются Материализованные представления (Materialized Views). Они автоматически создают физическую копию данных, отфильтрованную по определенным условиям, для ускорения чтения.
Простое представление
Создание материализованного представления для быстрого поиска по email:
CREATE MATERIALIZED VIEW employees_by_email AS
SELECT employee_id, first_name, last_name, email, salary
FROM employees
WHERE email IS NOT NULL AND employee_id IS NOT NULL
PRIMARY KEY (email, employee_id);
Использование представления:
SELECT * FROM employees_by_email WHERE email = 'ivanov@example.com';
Преимущества использования Materialized Views
- Автоматическое обновление при изменении исходной таблицы.
- Возможность выполнения быстрых запросов по вторичным ключам.
- Упрощение логики приложения.
Ограничения
- Нельзя создавать представления с агрегатными функциями.
- Нельзя использовать в таблицах с очень высокой нагрузкой на запись (повышают нагрузку).
- Не поддерживают сложные условия
WHEREкроме равенства.
Создание функций и процедур с агрегатными функциями и Join
В Cassandra отсутствуют хранимые процедуры, триггеры и операторы JOIN в классическом понимании. Логика агрегации и соединения данных должна быть реализована на уровне приложения или через специальные конструкции CQL.
Функция с агрегатными функциями
Агрегация возможна только внутри одной партиции. Для расчета средней зарплаты в отделе используется запрос к таблице, где department_id является Partition Key.
SELECT AVG(salary) as avg_salary
FROM employees_by_dept
WHERE department_id = 1;
Результат будет вычислен на стороне сервера Cassandra, но только для данных в пределах указанной партиции.
Эмуляция JOIN через приложение
Cassandra не поддерживает JOIN между таблицами. Для получения данных из разных таблиц (например, сотрудники и отделы) необходимо выполнить два отдельных запроса и объединить результаты в коде приложения.
Шаг 1: Получение списка отделов
SELECT id, name FROM departments WHERE id IN (1, 2);
Шаг 2: Получение сотрудников
SELECT * FROM employees_by_dept WHERE department_id IN (1, 2);
Шаг 3: Объединение в коде (Python пример)
departments = {d['id']: d['name'] for d in dept_rows}
for emp in emp_rows:
emp['dept_name'] = departments.get(emp['department_id'])
Триггерная логика (Validation Rules)
Прямых триггеров в Cassandra нет. Проверка условий осуществляется:
- На стороне приложения: Перед отправкой запроса.
- Через UDF (User Defined Functions): Можно создать пользовательскую функцию, но она не может выполнять вставку/удаление, только вычисления.
Пример функции проверки зарплаты:
CREATE FUNCTION check_salary_validity(salary DECIMAL)
RETURNS NULL ON NULL INPUT
RETURNS DECIMAL
LANGUAGE java
AS 'if (salary < 0) throw new IllegalArgumentException("Salary cannot be negative"); return salary;';
Использование функции в запросе (не рекомендуется для вставки, только для выборки):
SELECT check_salary_validity(salary) FROM employees WHERE employee_id = ...;
Для строгой валидации вставки лучше использовать IF условия в запросе INSERT или UPDATE:
INSERT INTO employees (...) VALUES (...) IF salary >= 0;